<?php

class GroundService extends AbstractService
{

    /**
     * @var GroundService
     */
    private static $instance;

    /**
     * Get the instance of GroundService.
     *
     * @return GroundService
     */
    public static function get()
    {
        if (!self::$instance) {
            self::$instance = new GroundService();
        }
        return self::$instance;
    }

    /**
     * Construction.
     */
    protected function __construct()
    {
        parent::__construct();
    }

    /**
     * 处理用户创建时的逻辑
     *
     * @param int $shardId
     * @return void
     */
    public function handleCreate($shardId)
    {
        // do nothing
    }

    /**
     * 处理用户登录时的逻辑
     *
     * @param int $userId
     * @return void
     */
    public function handleLogin($userId)
    {
        // do nothing
    }

    /**
     * 提供当前 service 数据
     *
     * @param int $userId
     * @param boolean $visitMode default false
     * @return array(GroundVoList)
     */
    public function info($userId, $visitMode = false)
    {
        $groundList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_GROUND);

        return array(
            $groundList
        );
    }

    /**
     * 验证地图上某一个格子的某一层是否已经被占用
     * <pre>
     *  当 layerId 设定为 -1 时，检查 ground 的全部图层.
     * </pre>
     *
     * @param GroundVo $ground
     * @param int $layerId default -1
     * @param boolean $errWhenOccupied default false
     * @throws 20160
     * @return boolean
     */
    public function isLayerOccupied($ground, $layerId = -1, $errWhenOccupied = false)
    {
        $result = false;

        if (!$ground) {
            return $result;
        }

        if (is_null($layerId) || $layerId == -1) {
            for ($loopLayerId = 1; $loopLayerId <= 3; ++$loopLayerId) {
                if (!is_null($ground->getLayer($loopLayerId)) && $ground->getLayer($loopLayerId) != -1) {
                    $result = true;
                    break;
                }
            }
        } else {
            if (!is_null($ground->getLayer($layerId)) && $ground->getLayer($layerId) != -1) {
                $result = true;
            }
        }

        if ($errWhenOccupied && $result) {
            throw new SzException(20160, $ground->getGridId());
        }

        return $result;
    }

    /**
     * 判断是否为有效地块
     *
     * @param int $userId
     * @param array $gridIds
     * @param array $blockIds
     * @param int $verifyType 地块验证类型，参考 GameConst::GROUND_VERIFY_TYPE_*
     * @param boolean $throwErr default true
     * @throws SzException 20104, 20107, 20108
     * @return boolean
     */
    public function isGridValid($userId, $gridIds, $blockIds, $verifyType, $throwErr = true)
    {
        // 验证 gridIds 的数据是否正确
        if (count($gridIds) < 2) {
            if ($throwErr) {
                throw new SzException(20165, json_encode($blockIds));
            } else {
                return false;
            }
        }

        $blockSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_BLOCK, null, true);
        $areaSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_AREA, null, true);

        $areaIds = array();
        foreach ($blockIds as $blockId) {
            // 验证地块解锁
            if (!$this->isBlockUnlock($userId, $blockId)) {
                throw new SzException(20163, $blockId);
            }
            // 验证地块配置
            if (!SzUtility::checkArrayKey($blockId, $blockSettings)) {
                throw new SzException(20164, $blockId);
            }
            $blockSetting = $blockSettings[$blockId];

            // 验证地块类型
            if ($blockSetting['blockType'] != $verifyType) {
                throw new SzException(20164, $blockId);
            }

            $areaIds = array_merge($blockSetting['areaId'], $areaIds);
        }

        // 计算 area 边界
        $startingX = 1;
        $endingX = 1;
        $startingY = 1;
        $endingY = 1;
        foreach ($areaIds as $areaId) {
            if (!SzUtility::checkArrayKey($areaId, $areaSettings)) {
                continue;
            }
            $areaConfig = $areaSettings[$areaId];
            $startingX < $areaConfig['startingX'] ?: $startingX = $areaConfig['startingX'];
            $startingY < $areaConfig['startingY'] ?: $startingY = $areaConfig['startingY'];
            $endingX > $areaConfig['endingX'] ?: $endingX = $areaConfig['endingX'];
            $endingY > $areaConfig['endingY'] ?: $endingY = $areaConfig['endingY'];
        }

        // 解析 gridIds 的坐标
        list($left, $right) = $gridIds;
        list($leftX, $leftY) = $this->buildCoordinate($left);
        list($rightX, $rightY) = $this->buildCoordinate($right);

        // 判断区域边界
        if ($leftX >= $startingX
            && $leftY >= $startingY
            && $rightX <= $endingX
            && $rightY <= $endingY
        ) {
            return true;
        } else {
            if ($throwErr) {
                throw new SzException(20165, json_encode($blockIds));
            } else {
                return false;
            }
        }
    }

    /**
     * 验证地块解锁
     *
     * @param int $userId
     * @param int $blockId
     * @param array $unlockBlockIds default null
     * @return bool
     */
    protected function isBlockUnlock($userId, $blockId, $unlockBlockIds = null)
    {
        if (!$unlockBlockIds) {
            $unlockBlockIds = UserService::get()->getInfo($userId, GameConst::USER_INFO_TYPE_BLOCK);
            $unlockBlockIds = (!$unlockBlockIds) ? array() : $unlockBlockIds;
        }

        return in_array($blockId, $unlockBlockIds);
    }

    /**
     * Check & get ground & element records.
     *
     * @param int $userId
     * @param int $gridId 格子id
     * @param int $gridLayer 格子id上的层级
     * @param int $layerType default GameConst::GROUND_LAYER_TYPE_DECORATION 地图格子的物品类型
     * @param GroundVoList $groundList default null
     * @throws SzException 20104
     * @return array
     * <pre>
     * array(elementId, GroundVo, GroundVoList)
     * </pre>
     */
    public function getGroundRecord($userId, $gridId, $gridLayer, $layerType = GameConst::GROUND_LAYER_TYPE_DECORATION, $groundList = null)
    {
        if (is_null($gridLayer) && (!is_numeric($gridLayer) || $gridLayer < 1 || $gridLayer > 3 || is_float($gridLayer))) {
            throw new SzException(20156, $gridLayer);
        }

        if (!$groundList) {
            $groundList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_GROUND);
        }

        /** @var GroundVo $ground */
        $ground = $groundList->getElement($gridId);

        // 验证gridId上的元素是否有效
        if ($ground->getLayerType($gridLayer) != $layerType) {
            throw new SzException(20157, array($gridId, $layerType, $gridLayer));
        }

        return array($ground->getLayer($gridLayer), $ground, $groundList);
    }

    /**
     * Remove the ground record if no any items stored in it.
     * Or there is some items in the ground, then just remove
     * current layer's decoration record.
     *
     * @param int $layerId
     * @param GroundVo $ground
     * @param GroundVoList $groundList
     * @throws SzException
     */
    public function clearGround($layerId, $ground, $groundList)
    {
        if ($layerId < 0 && (!is_numeric($layerId) || $layerId < 1 || $layerId > 3 || is_float($layerId))) {
            throw new SzException(20159, $layerId);
        }

        // 清除地块信息
        $ground->setLayer($layerId, -1);
        $ground->setLayerType($layerId, -1);

        $gridId = $ground->getGridId();
        if ($ground->isInsert() && !SzUtility::checkArrayKey($gridId, $groundList->getList())) {
            // do nothing
        } else {
            $groundList->updateElement($gridId, $ground);
        }
    }

    /**
     * 创建 string 类型坐标 AxB
     *
     * @param int $x
     * @param int $y
     * @return string
     */
    public function parseCoordinate($x, $y)
    {
        return $x . "x" . $y;
    }

    /**
     * 创建 array 类型坐标 AxB
     *
     * @param string $coordinate
     * @return array
     */
    public function buildCoordinate($coordinate)
    {
        list($x, $y) = explode('x', $coordinate);
        return array($x, $y);
    }

    /**
     * 扩展地块
     *
     * @param int $userId
     * @param int $blockId
     * @return boolean
     * @throws SzException
     */
    public function extend($userId, $blockId)
    {
        // 验证用户等级
        $blockSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_BLOCK, $blockId, true);

        if (UserService::get()->getUserLevel($userId) < $blockSetting['unlockLv']) {
            throw new SzException(20153, $blockId);
        }

        // 已经解锁的地块
        $unlockBlockIds = UserService::get()->getInfo($userId, GameConst::USER_INFO_TYPE_BLOCK);
        $unlockBlockIds = (!$unlockBlockIds) ? array() : $unlockBlockIds;
        //已解锁地块数量
        $unlockBlockCount = count($unlockBlockIds);

        // 验证前置解锁
        $isPerUnlock = false;
        $noUnlockPer = [];
        foreach ($blockSetting['preBlock'] as $preBlockId) {
            if (!in_array($preBlockId, $unlockBlockIds) && $preBlockId != -1) {
                $noUnlockPer[] = $preBlockId;
            } else {
                $isPerUnlock = true;
                break;
            }
        }
        if (!$isPerUnlock) {
            throw new SzException(20154, array($blockId, json_encode($noUnlockPer, true)));
        }
        // 验证前置建筑物的建造数量
        foreach ($blockSetting['preBuilding'] as $preBuildingId) {
            if (BuildingService::get()->getBuildingCount($userId, $preBuildingId) <= 0 && $preBuildingId != -1) {
                throw new SzException(20162, array($blockId, $preBuildingId));
            }
        }

        // 判断blockId是否已经解锁
        if ($this->isBlockUnlock($userId, $blockId, $unlockBlockIds)) {
            throw new SzException(20155, $blockId);
        }

        $unlockBlockCount++;
        // 扣除扩地道具
        $tid = '';
        $unlockBlockCostConfig = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_BLOCK_COST, $unlockBlockCount, true);
        for ($i = 1; $i <= 5; $i++) {
            GameService::get()->handleEntity($userId, $unlockBlockCostConfig['unlockItemId_' . $i], $unlockBlockCostConfig['unlockItemQuantity_' . $i], true);
            if ($unlockBlockCostConfig['unlockItemId_' . $i] != -1 && $unlockBlockCostConfig['unlockItemQuantity_' . $i] != -1) {
                $tid .= $unlockBlockCostConfig['unlockItemId_' . $i] . ':' . abs($unlockBlockCostConfig['unlockItemQuantity_' . $i]) . '|';
            }
        }

        // 解锁地块后，将地块上的装饰物进行初始化
        BuildingService::get()->generateForBlockUnlock($userId, array($blockId));

        // 更新解锁地块
        $unlockBlockIds[] = $blockId;
        UserService::get()->updateProfileInfo($userId, GameConst::USER_INFO_TYPE_BLOCK, $unlockBlockIds);
        // 更新任务进度
        MissionService::get()->progress($userId, GameConst::ENTITY_ACTION_EXTEND_GROUND, GameConst::ENTITY_TYPE_NULL, '', 1);

        // 数据采集，相关数值计算
        if ($blockSetting['blockType'] == GameConst::GROUND_BLOCK_TYPE_SHOP) {
            $module = 'indoor';
        } else if ($blockSetting['blockType'] == GameConst::GROUND_BLOCK_TYPE_POND) {
            $module = 'pond';
        } else {
            $module = 'land';
        }

        // 数据采集:地块解锁
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:' . $module . '|action:expand|',
                'item' => ($module == 'indoor') ? $this->calcShopAcreage($unlockBlockIds) : '',
                'value' => 1
            )
        ));

        // 数据采集：地块解锁消耗
        if ($tid != '') {
            CommonFunctionService::get()->reportResourceUseDetails(99999977,$userId,0,$tid,'extend');//消耗资源上报
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_INPUT, array(
                'clientData' => array(
                    'tid' => $tid,
                    'item' => ($module == 'pond') ? $module . '_expand_' . $blockId : $module . '_expand',
                )
            ));
        }

        return true;
    }

    /**
     * 更新地块格子
     *
     * @param int $userId 用户id
     * @param string $gridId 网格id（‘10*5’）
     * @param int $layerId 层id
     * @param int $elementId 某点的物品id
     * @param null $ground
     * @param null $groundList
     * @param null $layerType
     * @throws SzException
     * @return null $groundList
     */
    public function updateGroundWithGridId($userId, $gridId, $layerId, $elementId, &$ground = null, $groundList = null, $layerType = null)
    {
        if (is_null($groundList)) {
            $groundList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_GROUND);
        }

        if (is_null($ground)) {
            $ground = $groundList->getElement($gridId, false);
        }

        /**
         * $ground可能是从外部传入的vo，并且该$ground可能已经存在与insertList，并且还未持久化
         * 当再次需要更新字段的时候，不再需要往insertList里插入
         */
        if (is_null($ground)) {
            $ground = new GroundVo($userId, $gridId, -1, -1, -1, -1, -1, -1, true);
        }

        if ($layerId > 0 && $layerType > 0 && !is_null($elementId)) {
            $ground->setLayer($layerId, $elementId);
            $ground->setLayerType($layerId, $layerType);
        } else {
            throw new SzException(20224, $layerId);
        }

        if ($ground->isInsert() && !SzUtility::checkArrayKey($gridId, $groundList->getList())) {
            $groundList->addElement($ground);
            $groundList->setElement($gridId, $ground);
        } else {
            $groundList->updateElement($gridId, $ground);
        }

        return $groundList;
    }

    /**
     * 计算已解锁花店室内面积
     *
     * @param array $unlockBlockIds
     * @return int
     */
    private function calcShopAcreage($unlockBlockIds)
    {
        $blockSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_BLOCK, null, true);
        $areaSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_AREA, null, true);

        $acreage = 0;
        foreach ($unlockBlockIds as $blockId) {
            if ($blockSettings[$blockId]['blockType'] != GameConst::GROUND_BLOCK_TYPE_SHOP) {
                continue;
            }

            foreach ($blockSettings[$blockId]['areaId'] as $areaId) {
                $acreage += ($areaSettings[$areaId]['endingX'] - $areaSettings[$areaId]['startingX'] + 1) * ($areaSettings[$areaId]['endingY'] - $areaSettings[$areaId]['startingY'] + 1);
            }
        }
        return $acreage;
    }
}